using IdentityServer4;
using IdentityServer4.Models;
using System.Collections.Generic;
using System.Security.Claims;

namespace Idp_Login
{
    public partial class Config
    {
        /// <summary>
        /// clients want to access resources (aka scopes)
        /// 客户想要访问资源（也称为作用域）
        /// 定义客户端和客户端可以访问的Scope
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            //注册Client
            var clientList = new List<Client>();
            clientList.AddRange(Config_Clients_Demo.GetClientDemos());
            #region 01客户端认证 client credentials flow client

            var client01 = new Client
            {
                ClientId = "consoleClient", //访问客户端Id,必须唯一, v4版本不要有空格,有空格在postman中模拟不出来
                ClientName = "Client Credentials Client",

                AllowedGrantTypes = GrantTypes.ClientCredentials, //使用客户端授权模式，客户端只需要clientid和secrets就可以访问对应的api资源。

                //ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) },
                ClientSecrets = { new Secret("123456".Sha256()) },

                //AllowedScopes = { "api1" }
                //表示 这个client 的 api的资源 哪些
                AllowedScopes =
                {
                    //OpenId 和 Profile 两个是获取 IdentifyData  (id数据,身份证认证数据)
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    //Profile还可以这样写:   profile   或   new  IdentityResources.Profile().Name

                    IdentityServerConstants.StandardScopes.Email,
                    IdentityServerConstants.StandardScopes.Address,
                    IdentityServerConstants.StandardScopes.Phone,
                    "api1", "api3",


                },

                AlwaysSendClientClaims = true,
                #region v4版本的没有了 , 在代码 的 DefaultClientClaimsAdder 
                ////在此属性上设置的值将会被直接添加到AccessToken
                ////Client这里设置的Claims默认都会被带一个client_前缀
                ////[Authorize(Roles ="admin")] 含义:使用Claim是Role=Admin的, 注意:是 new TestUser() 里的 claim  , 不是 client_role ( new Client() 里的 claim )
                //Claims = new List<Claim>()
                //{
                //    //new Claim(JwtClaimTypes.Role, "admin")//表示 当前 client有一个 证件信息(Claim), 证件信息的key 是 role, value 是admin.  拥有 证件信息 并不代表它一定会使用,是否使用,在  api 中的 Claims 中说明

                //    new Claim(IdentityModel.JwtClaimTypes.Role, "Admin"),
                //    new Claim(IdentityModel.JwtClaimTypes.NickName, "root"),
                //    new Claim("eMail", "10000@qq.com")
                //}
                #endregion

            };
            clientList.Add(client01);
            #endregion

            #region 02密码认证 ResourceOwnerPassword

            var client02 = new Client
            {
                /* 要包含的元素 
a unique client ID
a secret if needed
the allowed interactions with the token service (called a grant type)
a network location where identity and/or access token gets sent to (called a redirect URI)
a list of scopes (aka resources) the client is allowed to access
                 */

                ClientId = "password_client",
                ClientName = "password_client name", //可选
                ClientSecrets = { new Secret("password_client".Sha256()) }, //如果需要,  可选
                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, //这里使用的是通过用户名密码换取token的方式.


                AccessTokenLifetime = 3600,//设置AccessToken过期时间

                //RefreshTokenExpiration = TokenExpiration.Absolute,//刷新令牌将在固定时间点到期
                AbsoluteRefreshTokenLifetime = 2592000,//单位秒. RefreshToken的最长生命周期,默认30天
                RefreshTokenExpiration = TokenExpiration.Sliding,//刷新令牌时，将刷新RefreshToken的生命周期。RefreshToken的总生命周期不会超过AbsoluteRefreshTokenLifetime。
                SlidingRefreshTokenLifetime = 3600,//以秒为单位滑动刷新令牌的生命周期。
                //按照现有的设置，如果3600秒内没有使用RefreshToken，那么RefreshToken将失效。即便是在3600秒内一直有使用RefreshToken，RefreshToken的总生命周期不会超过30天。所有的时间都可以按实际需求调整。

                AllowOfflineAccess = true,//如果要获取refresh_tokens ,必须把AllowOfflineAccess设置为true

                //服务端可提供的Scopes
                AllowedScopes =
                {
                    //https://www.cnblogs.com/fengchao1000/p/9849806.html

                    //OpenId 和 Profile 是必须的(如果要获取id_token), 即 在 密码模式和客户端模式  时必须要 这2个
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,

                    //如果要获取 refresh_tokens ,必须在scopes中加上OfflineAccess
                    IdentityServerConstants.StandardScopes.OfflineAccess, //请求的scope 需要带上 offline_access

                    //Profile还可以这样写:   profile   或   new  IdentityResources.Profile().Name
                    IdentityServerConstants.StandardScopes.Email,
                    IdentityServerConstants.StandardScopes.Address,
                    IdentityServerConstants.StandardScopes.Phone,
                    "api1",
                    "CustomIdentityResource", //自定义的关联 在  Config_IdentityResources.cs
                }
            };
            clientList.Add(client02);
            #endregion

            #region 03授权码模式 AuthorizationCode

            var client03 = new Client
            {
                ClientId = "mvc_code_client",
                ClientName = "Code Client",
                ClientSecrets = new[] { new Secret("secret".Sha256()) },
                //AllowedGrantTypes = GrantTypes.CodeAndClientCredentials,
                AllowedGrantTypes = GrantTypes.Code,
                RedirectUris =
                {
                    "http://localhost:5002/signin-oidc",
                    "http://localhost:5002/Ids4/IndexCodeToken"
                }, //登录成功后返回的客户端地址
                PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" }, //注销登录后返回的客户端地址

                //FrontChannelLogoutUri = "http://localhost:5002/signout-oidc",//指定客户端上用于基于HTTP前通道的注销的注销URI。 //https://www.cnblogs.com/stulzq/p/8570695.html

                AbsoluteRefreshTokenLifetime = 2592000,//单位秒. RefreshToken的最长生命周期,默认30天
                RefreshTokenExpiration = TokenExpiration.Sliding,//刷新令牌时，将刷新RefreshToken的生命周期。RefreshToken的总生命周期不会超过AbsoluteRefreshTokenLifetime。
                SlidingRefreshTokenLifetime = 3600,//以秒为单位滑动刷新令牌的生命周期。
                //按照现有的设置，如果3600秒内没有使用RefreshToken，那么RefreshToken将失效。即便是在3600秒内一直有使用RefreshToken，RefreshToken的总生命周期不会超过30天。所有的时间都可以按实际需求调整。

                RequireConsent = false, //如果不需要显示否同意授权 页面 这里就设置为false
                //AlwaysIncludeUserClaimsInIdToken = true,//当同时请求ID令牌和访问令牌时，用户声明应始终添加到ID令牌中，而不是要求客户端使用userinfo端点。默认为false。   说人话:将用户所有的claims包含在IdToken内

                AllowOfflineAccess = true, //   获取或设置一个值，该值指示是否[允许离线访问]。默认为false。
                AccessTokenLifetime = 3600, // 60 seconds //访问令牌的生存时间（以秒为单位）（默认为3600秒/ 1小时）
                AllowAccessTokensViaBrowser = true, //默认false ,true:允许将token通过浏览器传递
                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,

                    IdentityServerConstants.StandardScopes.Email,
                    IdentityServerConstants.StandardScopes.Address,
                    IdentityServerConstants.StandardScopes.Phone,
                    "api1",
                    IdentityServerConstants.StandardScopes.OfflineAccess, //如果要获取refresh_tokens ,必须在scopes中加上OfflineAccess
                }
            };
            clientList.Add(client03);
            #endregion

            #region 04简化模式 implicit

            var client04 = new Client
            {

                ClientId = "mvc client b", //客户端惟一标识
                ClientName = "ASP.NET Core MVC Client b",

                AllowedGrantTypes = GrantTypes.Implicit, //隐藏模式
                ClientSecrets = { new Secret("mvc secret".Sha256()) }, //不需要, 因为是账号密码登陆的,配置了也没什么作用

                RedirectUris =
                {
                    "http://localhost:5002/signin-oidc",
                    "http://localhost:5002/Ids4/IndexToken"
                }, //可以多个，根据请求来的转发

                PostLogoutRedirectUris =
                {
                    "http://localhost:5002/signout-callback-oidc"
                },
                FrontChannelLogoutUri = "http://localhost:5002/signout-oidc", //指定客户端上用于基于HTTP前通道的注销的注销URI。 //https://www.cnblogs.com/stulzq/p/8570695.html

                AllowAccessTokensViaBrowser = true, //默认false ,true:允许将token通过浏览器传递

                RequireConsent = false, //如果不需要显示否同意授权 页面 这里就设置为false 

                //AlwaysIncludeUserClaimsInIdToken = true,

                //AllowOfflineAccess = true, // offline_access
                //AccessTokenLifetime = 60, // 60 seconds

                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,

                    IdentityServerConstants.StandardScopes.Email,
                    IdentityServerConstants.StandardScopes.Address,
                    IdentityServerConstants.StandardScopes.Phone,
                    "api1",
                },


            };
            clientList.Add(client04);
            #endregion

            #region 05混合模式Hybrid

            var client05 = new Client
            {
                //ClientId = "hybrid_client",
                ClientId = "mvc",
                ClientName = "Hybrid Client",
                ClientSecrets = new[] { new Secret("secret".Sha256()) },
                AllowedGrantTypes = GrantTypes.Hybrid,
                //是否显示授权提示界面
                RequireConsent = false, //为true 时, 要单独写 controller + 页面
                RedirectUris =
                {
                    "http://localhost:5002/signin-oidc",
                    "http://localhost:5002/Ids4/IndexToken"
                },
                AllowAccessTokensViaBrowser = true, //默认false ,true:允许将token通过浏览器传递
                PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
                AllowedScopes =
                {
                    "api1", //资源范围
                    IdentityServerConstants.StandardScopes.OpenId, // OIDC的东西, 获取 Id_token 必须加入 "OpenId"
                    IdentityServerConstants.StandardScopes.Profile, // OIDC的东西,用户信息范围, 
                    //"CustomIdentityResource", //自定义的用户信息范围,在id_token中的
                },

                AlwaysIncludeUserClaimsInIdToken = true, //自定义的Id_Token 需要设置为true  天坑 .调试了真.1天,才测试出来

                AllowOfflineAccess = true,

                RequirePkce = false, //升级到v4, 网页登录需要添加这个, 否则 会提示  code_challenge is missing  和  Request validation failed
            };
            clientList.Add(client05);
            #endregion

            #region 06混合模式测试
            clientList.Add(new Client
            {
                //ClientId = "hybrid_client",
                ClientId = "good_client",
                ClientName = "good Client",
                ClientSecrets = new[] { new Secret("good_secret".Sha256()) },
                AllowedGrantTypes = GrantTypes.Hybrid,
                //是否显示授权提示界面
                RequireConsent = false,//为true 时, 要单独写 controller + 页面
                RedirectUris = { "http://localhost:5003/signin-oidc" },
                PostLogoutRedirectUris = { "http://localhost:5003/signout-callback-oidc" },
                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "api1",
                    "good",
                }
            });

            clientList.Add(new Client
            {
                //ClientId = "hybrid_client",
                ClientId = "order_client",
                ClientName = "order Client",
                ClientSecrets = new[] { new Secret("order_secret".Sha256()) },
                AllowedGrantTypes = GrantTypes.Hybrid,
                //是否显示授权提示界面
                RequireConsent = false,//为true 时, 要单独写 controller + 页面
                RedirectUris = { "http://localhost:5004/signin-oidc" },
                PostLogoutRedirectUris = { "http://localhost:5004/signout-callback-oidc" },
                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "api1",
                    "good",
                    "order",
                }
            });
            #endregion

            return clientList;
        }
    }
}
